package com.util;

import net.sf.json.*;
import  nu.xom.*;
import org.apache.commons.lang.StringUtils;
import org.dom4j.DocumentHelper;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

import java.io.*;
import java.util.Map;
import java.util.TreeMap;

public class JsonToXMLUtils {
    /**
     * flag to be tolerant for incomplete namespace prefixes
     */
    private boolean namespaceLenient;
    private static final String[] EMPTY_ARRAY = new String[0];

    /**
     * Map of namespaces for root element
     */
    private Map rootNamespace = new TreeMap();
    /**
     * list of properties to be expanded from child to parent
     */
    private String[] expandableProperties;


    public String[] getExpandableProperties() {
        return expandableProperties;
    }

    public void setExpandableProperties(String[] expandableProperties) {
        this.expandableProperties = expandableProperties == null ? EMPTY_ARRAY : expandableProperties;
    }

    public JsonToXMLUtils() {
        setNamespaceLenient(true);
        setExpandableProperties(EMPTY_ARRAY);
    }


    public boolean isNamespaceLenient() {
        return namespaceLenient;
    }

    public void setNamespaceLenient(boolean namespaceLenient) {
        this.namespaceLenient = namespaceLenient;
    }

    private Element newElement(String name) {
        if (name.indexOf(':') != -1) {
            namespaceLenient = true;
        }
        return namespaceLenient ? new CustomElement(name) : new Element(name);
    }

    private static class CustomElement extends Element {
        private static String getName(String name) {
            int colon = name.indexOf(':');
            if (colon != -1) {
                return name.substring(colon + 1);
            }
            return name;
        }

        private static String getPrefix(String name) {
            int colon = name.indexOf(':');
            if (colon != -1) {
                return name.substring(0, colon);
            }
            return "";
        }

        private String prefix;

        public CustomElement(String name) {
            super(CustomElement.getName(name));
            prefix = CustomElement.getPrefix(name);
        }

        public final String getQName() {
            if (prefix.length() == 0) {
                return getLocalName();
            } else {
                return prefix + ":" + getLocalName();
            }
        }
    }


    public String write(JSON json) {
        return write(json, null);
    }



    private String writeDocument( Document doc, String encoding ) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try{
            XomSerializer serializer = (encoding == null) ? new XomSerializer( baos )
                    : new XomSerializer( baos, encoding );
            serializer.write( doc );
            encoding = serializer.getEncoding();
        }catch( IOException ioe ){
            throw new JSONException( ioe );
        }

        String str = null;
        try{
            str = baos.toString( encoding );
        }catch( UnsupportedEncodingException uee ){
            throw new JSONException( uee );
        }
        return str;
    }

    private Element processJSONObject(JSONObject jsonObject, Element root,
                                      String[] expandableProperties, boolean isRoot) {
      if( jsonObject.isNullObject() ){
            return root;
        }else if( jsonObject.isEmpty() ){
            return root;
        }

        Object[] names = jsonObject.names().toArray();
        //Arrays.sort(names);
        Element element = null;
        for (int i = 0; i < names.length; i++) {

            String name = (String) names[i];
            //如果节点名称是remark,则掠过
            if(name.equals("remark")){
               continue;
            }
            Object value = jsonObject.get(name);

             if (value instanceof JSONArray ) {

                JSONArray array = (JSONArray) value;
                int l = array.size();
                for (int j = 0; j < l; j++) {
                    Object item = array.get(j);
                    element = newElement(name);
                    if (item instanceof JSONObject) {
                        element = processJSONValue((JSONObject) item, root, element,
                                expandableProperties);
                    } else if (item instanceof JSONArray) {
                        element = processJSONValue((JSONArray) item, root, element, expandableProperties);
                    } else {
                        element = processJSONValue(item, root, element, expandableProperties);
                    }
                 //   addNameSpaceToElement(element);
                    root.appendChild(element);
                }
            } else {
                element = newElement(name);
                element = processJSONValue(value, root, element, expandableProperties);
                //addNameSpaceToElement(element);
                root.appendChild(element);
            }
        }
        return root;
    }


    public String write(JSON json, String encoding) {

        JSONObject jsonObject = (JSONObject) json;

        Object[] rootNames = jsonObject.names().toArray();
      //  Arrays.sort(rootNames);
        String rootName = (String) rootNames[0];
        JSONObject rootValue = (JSONObject) jsonObject.get(rootName);
        //根节点
        Element root =newElement(rootName);

        if (!rootValue.isNullObject()) {
          /*  root = processJSONObject( jsonObject,
                    newElement( getRootName() == null ? getObjectName() : getRootName() ),
                    expandableProperties, true );*/

            root = processJSONObject(rootValue, root, expandableProperties, true);
        }

        Document doc = new Document( root );
        return writeDocument( doc, encoding );

    }



    private Element processJSONValue( Object value, Element root, Element target,
                                      String[] expandableProperties ) {
      /*  if( target == null ){
            target = newElement( getElementName() );
        }*/
      if( value instanceof JSONArray ){

           target = processJSONArray( (JSONArray) value, target, expandableProperties );

        }else if( value instanceof JSONObject ){

             target = processJSONObject( (JSONObject) value, target, expandableProperties, false );

        }else
             {

              target.appendChild( value.toString() );
        }
        return target;
    }

    private Element processJSONArray( JSONArray array, Element root, String[] expandableProperties ) {
        int l = array.size();
        for( int i = 0; i < l; i++ ){
            Object value = array.get( i );
            Element element = processJSONValue( value, root, null, expandableProperties );
            root.appendChild( element );
        }
        return root;
    }




    private class XomSerializer extends Serializer {
        public XomSerializer( OutputStream out ) {
            super( out );
        }

        public XomSerializer( OutputStream out, String encoding ) throws UnsupportedEncodingException {
            super( out, encoding );
        }


        protected void write( Text text ) throws IOException {
            String value = text.getValue();
            if( value.startsWith( "<![CDATA[" ) && value.endsWith( "]]>" ) ){
                value = value.substring( 9 );
                value = value.substring( 0, value.length() - 3 );
                writeRaw( "<![CDATA[" );
                writeRaw( value );
                writeRaw( "]]>" );
            }else{
                super.write( text );
            }
        }

        protected void writeEmptyElementTag( Element element ) throws IOException {
            if( element instanceof CustomElement && isNamespaceLenient() ){
                writeTagBeginning( (CustomElement) element );
                writeRaw( "/>" );
            }else{
                super.writeEmptyElementTag( element );
            }
        }

        protected void writeEndTag( Element element ) throws IOException {
            if( element instanceof CustomElement && isNamespaceLenient() ){
                writeRaw( "</" );
                writeRaw( ((CustomElement) element).getQName() );
                writeRaw( ">" );
            }else{
                super.writeEndTag( element );
            }
        }

        protected void writeNamespaceDeclaration( String prefix, String uri ) throws IOException {
            if( !StringUtils.isBlank( uri ) ){
                super.writeNamespaceDeclaration( prefix, uri );
            }
        }

        protected void writeStartTag( Element element ) throws IOException {
            if( element instanceof CustomElement && isNamespaceLenient() ){
                writeTagBeginning( (CustomElement) element );
                writeRaw( ">" );
            }else{
                super.writeStartTag( element );
            }
        }

        private void writeTagBeginning( CustomElement element ) throws IOException {
            writeRaw( "<" );
            writeRaw( element.getQName() );
            writeAttributes( element );
            writeNamespaceDeclarations( element );
        }


    }

    /**
     *  格式化输出XML字符串
     * @param str
     * @return
     * @throws Exception
     */
    public static String formatXml(String str) throws Exception {
        org.dom4j.Document document = null;
        document = DocumentHelper.parseText(str);
        // 格式化输出格式
        OutputFormat format = OutputFormat.createPrettyPrint();
        format.setEncoding("UTF-8");
        StringWriter writer = new StringWriter();
        // 格式化输出流
        XMLWriter xmlWriter = new XMLWriter(writer, format);
        // 将document写入到输出流
        xmlWriter.write(document);
        xmlWriter.close();

        return writer.toString();
    }



}
